﻿// Copyright (c) 2012 - 2020 Jared Zheng <jaredz at outlook dot com>
// Buddha Framework is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
//          http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.
//
// Create : 2012-12-01 version 0.1 Jared Zheng <jaredz at outlook dot com>
// Update : 2018-12-05 version 3.1.5 Jared Zheng <jaredz at outlook dot com>

#include "stdafx.h"
#include "CommonServer.h"
#include "authorpacket.h"
#include "servicepacket.h"
#include "RoutineImp.h"
#include "routinedef.h"

///////////////////////////////////////////////////////////////////////////////////////////////////////////
// CMasterRoutine
IMPLEMENT_RTTI_CREATE( CMasterRoutine, CObject, CObjectTraits::OBJECTT_NONE )

UInt CMasterRoutine::OnHandle(Int nEvent, uintptr_t utData, ULLong ullParam)
{
    if (nEvent == PAKE_UNLINK)
    {
        SERVER_UNLINK Unlink;  
        Unlink.utData = utData;
        if (Unlink.tiData.usType == PAKT_SERVICE)
        {
            ClearServiceSocket(Unlink.tiData.usId);

            ARY_ROUTINEID aRID;
            if (RemoveServiceAllRID(ullParam, aRID)) // 没有任务rid注册的不需要广播
            {
                LOGV_DUMP(m_pServer->GetFileLog(), TF("[业务管理例程]业务功能断开[%p] --- 连接的业务RID=%d, 由业务网关转发外网端断开给对应业务!"), ullParam, aRID.GetSize());
                CNETSrvUnlinkAck UnlinkAck((UACK)ROUTINE_ERROR_LINK_ONLINE);
                UnlinkAck.SetSrvId((USRVID)Unlink.tiData.usId);
                UnlinkAck.SetSessionId(ullParam);
                CStream& Stream = UnlinkAck.Alloc(m_pServer->GetNetworkPtr());
                Stream << (UInt)aRID.GetSize();
                Stream.Write(aRID.GetData(), sizeof(ULLong) * aRID.GetSize());
                UnlinkAck.BroadcastSend(m_pServer->GetNetworkPtr(), CNETTraits::SEND_BROADCAST_AS, PAKT_GATE);

                OnUnlinkAllRoutineId(aRID, Unlink.tiData.usId, ullParam);
            }
            else
            {
                LOGV_DUMP(m_pServer->GetFileLog(), TF("[业务管理例程]业务功能断开[%p] --- 没有连接的业务RID=%d!"), ullParam, aRID.GetSize());
            }
        }
        else if (Unlink.tiData.usType == PAKT_GATE)
        {
            LOGV_DUMP(m_pServer->GetFileLog(), TF("[业务管理例程]业务网关断开[%p] --- 通知所有业务功能服务器, 将对应外网端标记为服务器异常离线状态!"), ullParam);
            ClearGateSocket(Unlink.tiData.usId);

            CPAKSrvLogout Logout;
            Logout.SetType(PAKT_MASTER);
            Logout.SetSrvId((USRVID)Unlink.tiData.usId);
            Logout.SetSessionId(ullParam);
            m_pServer->GetNetworkPtr()->Send(0, Logout, CNETTraits::SEND_BROADCAST_AS, PAKT_SERVICE_MASTER);
        }
    }
    if (m_BusinessPtr != nullptr)
    {
        m_BusinessPtr->OnHandle(nEvent, utData, ullParam);
    }
    return RET_OKAY;
}

UInt CMasterRoutine::OnHandle(Int nEvent, CEventBase& EventRef, ULLong ullParam)
{
    switch (nEvent)
    {
    case PAKE_AUTHOR_VISA:
        {
            OnTransferAuthor(EventRef, ullParam);
        }
        break;
    case PAKE_SERVICE_LINK:   // 业务服务器注册id/网关重连
        {
            OnLinkRoutineId(EventRef, ullParam);
        }
        break;
    case PAKE_SERVICE_UNLINK: // 业务服务器注销id/网关注销
        {
            OnUnlinkRoutineId(EventRef, ullParam);
        }
        break;
    case PAKE_SERVICE_LOGIN: // 网关通知连接上管理, 需要知道在线的业务功能
        {
            CNETSrvLogin* pLogin = static_cast<CNETSrvLogin*>(&EventRef);
            pLogin->SetType((UTYPE)PAKT_MASTER);
            pLogin->BroadcastSend(m_pServer->GetNetworkPtr(), CNETTraits::SEND_BROADCAST_AS, PAKT_SERVICE_MASTER);
        }
        break;
    case PAKE_SERVICE_LOGOUT: // 外网端断开, 没有连接业务功能的不会通知业务管理
        {
            OnExternalLogout(EventRef, ullParam);
        }
        break;
    case PAKE_SERVICE_LOGINACK: // 在线的业务通知网关在线
        {
            CNETSrvLoginAck* pAck = static_cast<CNETSrvLoginAck*>(&EventRef);
            pAck->ResetHead((UEVENT)PAKE_SERVICE_LINKACK, (UTYPE)PAKT_MASTER, (UACK)ROUTINE_ERROR_LINK_ONLINE);
            Socket_t sSocket = GetSocket(PAKT_GATE, pAck->GetSrvId());
            if (sSocket != 0)
            {
                pAck->Send(sSocket, m_pServer->GetNetworkPtr());
            }
        }
        break;
    case PAKE_SERVICE_LOGOUTACK: // 踢除特定外网端
        {
            CNETSrvLogoutAck* pAck = static_cast<CNETSrvLogoutAck*>(&EventRef);
            pAck->SetType((UTYPE)PAKT_MASTER);
            Socket_t sSocket = GetSocket(PAKT_GATE, pAck->GetSrvId());
            if (sSocket != 0)
            {
                pAck->Send(sSocket, m_pServer->GetNetworkPtr());
            }
        }
        break;
    case PAKE_SERVICE_BROADCASTACK: // 广播
        {
            CNETSrvBroadcastAck* pAck = static_cast<CNETSrvBroadcastAck*>(&EventRef);
            pAck->SetType((UTYPE)PAKT_MASTER);

            DEV_DUMP(TF("[业务管理例程]收到广播数据包Flag=%d, Live-Data=%llX, size=%d"), pAck->GetSrvId(), pAck->GetSessionId(), pAck->Length());
            LOGV_DUMP(m_pServer->GetFileLog(), TF("[业务管理例程]收到广播数据包Flag=%d, Live-Data=%llX, size=%d"), pAck->GetSrvId(), pAck->GetSessionId(), pAck->Length());

            pAck->ResetBroadcastSend(m_pServer->GetNetworkPtr(), pAck->Length(), CNETTraits::SEND_BROADCAST_AS, PAKT_GATE);
        }
    case PAKE_SERVICE_MAINTAINACK:
        {
              // 广播给网关 时间有效并且和原来的不一样
        }
        break;
    default:
        {
            OnTransferRoutine(nEvent, EventRef, ullParam);
        }
    }
    if (m_BusinessPtr != nullptr)
    {
        m_BusinessPtr->OnHandle(nEvent, EventRef, ullParam);
    }
    return RET_OKAY;
}

UInt CMasterRoutine::OnHandle(Int nEvent, CStream& Stream, ULLong ullParam)
{
    if (m_BusinessPtr != nullptr)
    {
        m_BusinessPtr->OnHandle(nEvent, Stream, ullParam);
    }
    return RET_OKAY;
}

bool CMasterRoutine::Start(ICommonServer* pServer)
{
    m_pServer = pServer;
    if ((m_pServer != nullptr) && LoadExtConfig())
    {
        // 默认必须创建异步事件队列
        m_uThreads = (UInt)m_pServer->GetConfig().GetServerValue(CServerConfig::CFG_LOAD_MASTER);
        m_uThreads = CPlatform::CheckThread(m_uThreads);
        LOGV_INFO(m_pServer->GetFileLog(), TF("[业务管理例程]异步事件队列处理线程=%d, %s"), m_uThreads, (m_uThreads > 1) ? TF("启用多线程并发处理") : TF("启用单线程处理"));
        m_nUpdate = 0;
        // 超时为更新周期的倍数
        SetTimeout();

        m_RoutineId.Init();
        m_TypeRId.Init();

        m_RIdSocket.Init();
        m_SocketRId.SetHash(MAP_HASH_MIN);

        m_ServiceId.Init();
        m_GateId.Init();

        if (m_BusinessPtr != nullptr)
        {
            if (m_BusinessPtr->Start(pServer) == false) // 注意异步事件队列还未初始化
            {
                return false;
            }
        }
        if (CEventQueue::EventQueue(m_EventQueuePtr, *this, 0, (Int)m_uThreads))
        {
            m_EventQueuePtr->SetThreadEvent(PAKE_THREAD_START, PAKE_THREAD_STOP);
            return m_EventQueuePtr->Init();
        }
    }
    return false;
}

bool CMasterRoutine::Pause(bool bPause)
{
    if (m_BusinessPtr != nullptr)
    {
        m_BusinessPtr->Pause(bPause);
    }
    return false;
}

bool CMasterRoutine::Update(void)
{
    if (m_pServer->GetServerInfo(PAKT_MASTER)->IsMayDay() == false)
    {
        if (m_EventQueuePtr->CheckThreads(m_uTimeout))
        {
            m_pServer->GetServerInfo(PAKT_MASTER)->MayDay();
            DEV_ERROR(TF("[业务管理例程]事件队列线程异常, 进入异常状态"));
            LOG_ERROR(m_pServer->GetFileLog(), TF("[业务管理例程]事件队列线程异常, 进入异常状态"));
        }
    }
    // 定时更新线程不需要检测
    // 定时更新线程异常导致不向同步管理发送心跳包, 会导致本服务器离线

    // 定时检测扩展配置文件是否有修改
    ++m_nUpdate;
    if (m_nUpdate >= m_nTime)
    {
        m_nUpdate = 0;
        LoadExtConfig(true);
    }
    if (m_BusinessPtr != nullptr)
    {
        m_BusinessPtr->Update();
    }
    return true;
}

void CMasterRoutine::Stop(void)
{
    if (m_BusinessPtr != nullptr)
    {
        m_BusinessPtr->Stop();
        CString strName = m_BusinessPtr->GetRTTI().GetName();
        m_BusinessPtr->Exit();
        m_BusinessPtr = nullptr;
        CObjectTraits::Unload<ICommonRoutine>(*strName);
    }
    if (m_EventQueuePtr != nullptr)
    {
        m_EventQueuePtr->Exit();
        m_EventQueuePtr = nullptr;
    }
    m_RoutineId.Exit();
    m_TypeRId.Exit();

    m_RIdSocket.Exit();
    m_SocketRId.RemoveAll();

    m_ServiceId.Exit();
    m_GateId.Exit();

    m_pServer         = nullptr;
    m_nUpdate         = 0;
    m_uThreads        = 0;
    m_uTimeout        = 0;
    m_bEnableLink     = true;
    m_bTypeCheck      = false;
}

Socket_t CMasterRoutine::GetSocket(UInt uType, UInt uId)
{
    // 没有删除操作, 不需要加锁
    if (uType == PAKT_SERVICE)
    {
        ID_MAP_PAIR* pPair = m_ServiceId.GetCurr().Find(uId);
        if (pPair != nullptr)
        {
            return (Socket_t)pPair->m_V;
        }
    }
    else if (uType == PAKT_GATE)
    {
        ID_MAP_PAIR* pPair = m_GateId.GetCurr().Find(uId);
        if (pPair != nullptr)
        {
            return (Socket_t)pPair->m_V;
        }
    }
    return 0;
}

bool CMasterRoutine::SetTimeout(UInt uTimeout)
{
    if (uTimeout == 0)
    {
        m_uTimeout = m_pServer->GetConfig().GetUpdateTick() * DATAD_TIME;
    }
    else
    {
        m_uTimeout = uTimeout;
        m_uTimeout = DEF::Max<UInt>(1, m_uTimeout);
    }
    return true;
}

bool CMasterRoutine::AddQueue(Int nEvent, uintptr_t utData, ULLong ullParam, QUEUE_TYPE eType)
{
    if (m_pServer == nullptr)
    {
        return false;
    }
    // 入异步事件队列前的检测尽量不要处理复杂功能, 需要处理的建议进队列回调处理
    // 有加锁的建议进队列回调处理
    // "ServerAck"
    // PAKE_LINKACK/PAKE_UNLINKACK, id, center/masterdb
    // "ServerInfo"
    // PAKE_UNLINK, PSERVER_STATUS(id, status-live-data), socket // 通知所有网关业务服务器断开, 通知所有业务网关断开
    bool bRet = false;
    if (m_BusinessPtr != nullptr)
    {
        bRet = m_BusinessPtr->AddQueue(nEvent, utData, ullParam, eType);
    }
    switch (nEvent)
    {
    case PAKE_LINKACK:    // 向管理服务器注册回复结果
    case PAKE_UNLINK:     // 向本服务器注册的注销/断开
    case PAKE_UNLINKACK:  // 向管理服务器注销/断开回复结果
    case PAKE_TCP_CLOSED: // 连接的外网端注销/断开
        {
            bRet = true;
        }
        break;
    default: {}
    }
    if (bRet)
    {
        bRet = m_EventQueuePtr->Add(nEvent, utData, ullParam, eType);
    }
    return bRet;
}

bool CMasterRoutine::AddQueue(Int nEvent, CEventBase& EventRef, ULLong ullParam, QUEUE_TYPE eType)
{
    if (m_pServer == nullptr)
    {
        return false;
    }
    // 入异步事件队列前的检测尽量不要处理复杂功能, 需要处理的建议进队列回调处理
    // 有加锁的建议进队列回调处理
    // "ServerInfo"
    // PAKE_LINK/PAKE_UPDATE, EventRef, PTCP_PARAM
    // "CommonEvent"
    // [PAKE_COMMON_BEGIN, PAKE_COMMON_END], EventRef, PTCP_PARAM/PUDP_PARAM
    //
    // [PAKE_ROUTINE_BEGIN, PAKE_ROUTINE_FAIL], EventRef, PTCP_PARAM
    // [PAKE_SERVICE_BEGIN, PAKE_SERVICE_END], EventRef, PTCP_PARAM
    // [PAKE_AUTHOR_BEGIN, PAKE_AUTHOR_ACK), EventRef, PUDP_PARAM
    bool bRet = false;
    if (m_BusinessPtr != nullptr)
    {
        bRet = m_BusinessPtr->AddQueue(nEvent, EventRef, ullParam, eType);
    }
    ULLong ullSocket = ullParam;
    if (eType > QUEUE_TYPE_MASK)
    {
        CNETTraits::PTCP_PARAM pTcp = reinterpret_cast<CNETTraits::PTCP_PARAM>(ullParam);
        ullSocket = pTcp->sSocket;
    }
    if ((nEvent >= PAKE_ROUTINE_BEGIN) && (nEvent <= PAKE_ROUTINE_FAIL))
    {
        bRet = true;
        (static_cast<CNETRoutine*>(&EventRef))->Attach(ullParam, eType, m_pServer->GetNetworkPtr());
    }
    else if ((nEvent >= PAKE_SERVICE_BEGIN) && (nEvent <= PAKE_SERVICE_END))
    {
        bRet = true;
        (static_cast<CNETSession*>(&EventRef))->Attach(ullParam, eType, m_pServer->GetNetworkPtr());
    }
    // Author
    else if ((nEvent >= PAKE_AUTHOR_BEGIN) && (nEvent < PAKE_AUTHOR_ACK))
    {
        bRet = true;
        (static_cast<CNETSession*>(&EventRef))->Attach(ullParam, eType, m_pServer->GetNetworkPtr());
    }
    if (bRet)
    {
        bRet = m_EventQueuePtr->Add(nEvent, EventRef, ullSocket, (eType & QUEUE_TYPE_MASK));
    }
    if (nEvent == PAKE_LINK)
    {
        CNETLink* pLink = static_cast<CNETLink*>(&EventRef);
        if (pLink->GetType() == PAKT_SERVICE)
        {
            bRet = SetServiceSocket(pLink->GetServerStatus().usId, ullSocket);
        }
        else
        {
            assert(pLink->GetType() == PAKT_GATE);
            bRet = SetGateSocket(pLink->GetServerStatus().usId, ullSocket);
        }
        if (bRet == false)
        {
            LOGV_WARN(m_pServer->GetFileLog(), TF("[业务管理例程]%p服务器类型[%d]注册时对应的id=%d已经有服务器在线"), ullSocket, pLink->GetType(), pLink->GetServerStatus().usId);
            pLink->Attach(ullParam, eType, m_pServer->GetNetworkPtr());
            pLink->ResetHead((UEVENT)PAKE_LINKACK, (UTYPE)PAKT_MASTER, (UACK)ROUTINE_ERROR_VOIDNESS_SRVID);
            pLink->Send(ullSocket, m_pServer->GetNetworkPtr());
        }
    }
    return bRet;
}

bool CMasterRoutine::AddQueue(Int nEvent, CStream& Stream, ULLong ullParam)
{
    if (m_pServer == nullptr)
    {
        return false;
    }
    // 入异步事件队列前的检测尽量不要处理复杂功能, 需要处理的建议进队列回调处理
    // 有加锁的建议进队列回调处理
    // "SyncInfo"---注意流对象可能是临时实例, 不能加入队列
    // PAKE_LINK/PAKE_UPDATE, Stream, center/masterdb
    bool bRet = false;
    if (m_BusinessPtr != nullptr)
    {
        bRet = m_BusinessPtr->AddQueue(nEvent, Stream, ullParam);
    }
    if (bRet)
    {
        bRet = m_EventQueuePtr->Add(nEvent, Stream, ullParam);
    }
    return bRet;
}

bool CMasterRoutine::LoadExtConfig(bool bUpdate)
{
    CStringFix strPath;
    m_pServer->GetConfig().GetServerExtConfig(CServerConfig::CFG_LOAD_MASTER, strPath);

    CFileAttr fa;
    m_pServer->GetConfig().GetFilePath().FileAttr(fa, *strPath);
    if (bUpdate)
    {
        if (fa.MTime(m_tExtConfig))
        {
            return true;
        }
        LOGV_INFO(m_pServer->GetFileLog(), TF("[业务管理例程]定时读取文件修改时间%llX---%llX"), fa.MTime(), m_tExtConfig);
        m_tExtConfig = fa.MTime();
    }
    else
    {
        m_tExtConfig = fa.MTime();
        LOGV_INFO(m_pServer->GetFileLog(), TF("[业务管理例程]初始化读取文件修改时间=%llX"), m_tExtConfig);
    }
    LOGV_INFO(m_pServer->GetFileLog(), TF("[业务管理例程]ExtConfig=%s"), *strPath);

    CFileReadStream Reader;
    if (Reader.Create(*strPath))
    {
        CKVNode  KVNode;
        CKVStore KVStore;
        if (KVStore.Load(KVNode, Reader))
        {
            if (KVNode.Find(TF("Time")))
            {
                m_nTime = KVNode[TF("Time")].GetInt();
            }
            if (KVNode.Find(TF("RoutineLink")))
            {
                m_bEnableLink = KVNode[TF("RoutineLink")].GetBoolean();
                LOGV_INFO(m_pServer->GetFileLog(), TF("[业务管理例程]RoutineLink=%d"), m_bEnableLink);
            }

            if (KVNode.Find(TF("RoutineId")))
            {
                LoadExtRoutineId(KVNode, bUpdate);
            }
            //if (KVNode.Find(TF("SessionType")))
            //{
            //    LoadExtSessionType(KVNode, bUpdate);
            //}
            bool bRet = true;
            if ((bUpdate == false) && (KVNode.Find(TF("BusinessRoutine"))))
            {
                bRet = LoadExtBusiness(KVNode);
            }
            if (m_BusinessPtr != nullptr)
            {
                bRet = (m_BusinessPtr->OnHandle(bUpdate ? PAKE_CONFIG_UPDATE : PAKE_CONFIG_LOAD,
                                                reinterpret_cast<uintptr_t>(&KVNode),
                                                (ULLong)m_pServer) == RET_OKAY);
            }
            return bRet;
        }
        else
        {
            LOGV_WARN(m_pServer->GetFileLog(), TF("[业务管理例程]解析ExtConfig=%s文件内容失败"), *strPath);
        }
    }
    else
    {
        LOGV_WARN(m_pServer->GetFileLog(), TF("[业务管理例程]打开ExtConfig=%s文件不存在"), *strPath);
    }
    return true;
}

bool CMasterRoutine::LoadExtRoutineId(CKVNode& Root, bool bUpdate)
{
    CSyncLockWaitScope scope(m_RoutineId.Lock);
    ROUTINEID_MAP& RoutineId = m_RoutineId.GetNextClone();

    CKVString strRoutineId;

    CKVNode& kvRoutineId = Root[TF("RoutineId")];
    Int nSize = kvRoutineId.GetSize();
    for (Int i = 0; i < nSize; ++i)
    {
        if (kvRoutineId[i].GetString(strRoutineId))
        {
            ULLong ullHash = CHash::Hash64(*strRoutineId);
            ROUTINEID_MAP_PAIR* pPair = RoutineId.Find(ullHash);
            if (bUpdate)
            {
                // 更新无法删除业务功能ID
                if (pPair == nullptr)
                {
                    RoutineId.Add(ullHash, std::move(strRoutineId));
                    LOGV_DUMP(m_pServer->GetFileLog(), TF("[业务管理例程]RoutineId load2 --- UUID=%s[HASH=%llX]成功加入列表"), *strRoutineId, ullHash);
                }
                else if (pPair->m_V != strRoutineId)// uuid-hash冲突
                {
                    LOGV_WARN(m_pServer->GetFileLog(), TF("[业务管理例程]RoutineId load2 --- UUID=%s[HASH=%llX]与存在的UUID=%s[HASH=%llX] --- HASH值冲突, %s被忽略, 请重新更换UUID!"), *strRoutineId, ullHash, *(pPair->m_V), pPair->m_K, *strRoutineId);
                }
            }
            else
            {
                if (pPair == nullptr)
                {
                    RoutineId.Add(ullHash, std::move(strRoutineId));
                    LOGV_DUMP(m_pServer->GetFileLog(), TF("[业务管理例程]RoutineId load --- UUID=%s[HASH=%llX]成功加入列表"), *strRoutineId, ullHash);
                }
                else // uuid-hash冲突
                {
                    LOGV_WARN(m_pServer->GetFileLog(), TF("[业务管理例程]RoutineId load --- UUID=%s[HASH=%llX]与存在的UUID=%s[HASH=%llX] --- HASH值冲突, %s被忽略, 请重新更换UUID!"), *strRoutineId, ullHash, *(pPair->m_V), pPair->m_K, *strRoutineId);
                }
            }
        }
    }
    m_RoutineId.Swap();
    return true;
}

bool CMasterRoutine::LoadExtSessionType(CKVNode& Root, bool bUpdate)
{
    CSyncLockWaitScope scope(m_TypeRId.Lock);
    TYPE_RID_MAP& TypeRId = m_TypeRId.GetNextClone();

    CKVString strType;
    CKVString strRoutineId;

    CKVNode& SessionType = Root[TF("SessionType")];
    for (PINDEX index = SessionType.First(); index != nullptr; )
    {
        SessionType.Next(strType, index);
        UInt uType = (UInt)strType.ToULong();
        TYPE_RID_MAP_PAIR* pPair =  TypeRId.Find(uType);
        if (bUpdate)
        {
            // 更新无法删除Type, 只会覆盖原来的, 如果有多个相同Type, 最后的数据将替换前面的数据
            if (pPair == nullptr)
            {
                PINDEX inxAdd = TypeRId.Add(uType);
                if (inxAdd != nullptr)
                {
                    pPair = TypeRId.GetAt(inxAdd);
                }
                LOGV_DUMP(m_pServer->GetFileLog(), TF("[业务管理例程]SessionType load2 --- Type=%s成功加入列表"), *strType);
            }
        }
        else
        {
            if (pPair == nullptr)
            {
                PINDEX inxAdd = TypeRId.Add(uType);
                if (inxAdd != nullptr)
                {
                    pPair = TypeRId.GetAt(inxAdd);
                }
                LOGV_DUMP(m_pServer->GetFileLog(), TF("[业务管理例程]SessionType load --- Type=%s成功加入列表"), *strType);
            }
            else
            {
                LOGV_WARN(m_pServer->GetFileLog(), TF("[业务管理例程]SessionType load --- Type=%s值冲突, 对应数据被忽略, 请重新更换Type!"), *strType);
                continue;
            }
        }
        ARY_ROUTINEID aRoutineId;

        CKVNode& RoutineId = SessionType[*strType];
        Int nSize = RoutineId.GetSize();
        for (Int i = 0; i < nSize; ++i)
        {
            if (RoutineId[i].GetString(strRoutineId))
            {
                aRoutineId.Add(CHash::Hash64(*strRoutineId));
            }
        }
        LOGV_DUMP(m_pServer->GetFileLog(), TF("[业务管理例程]SessionType load --- Type=%s的默认连接业务ID个数有%d个"), *strType, aRoutineId.GetSize());
        pPair->m_V = std::move(aRoutineId);
    }
    m_TypeRId.Swap();
    return true;
}

bool CMasterRoutine::LoadExtBusiness(CKVNode& Root)
{
    CKVString strName;
    CKVString strObject;
    CKVString strModule;

    CKVNode& Routine = Root[TF("BusinessRoutine")];
    Routine[TF("Name")].GetString(strName);
    Routine[TF("Object")].GetString(strObject);
    Routine[TF("Module")].GetString(strModule);
#if (__PLATFORM_TARGET__ == PLATFORM_TARGET_LINUX)
    strModule.Insert(0, CServerConfig::LibPrefix);
#endif
    strModule += CServerConfig::LibExt;

    assert(m_BusinessPtr == nullptr);
    if (CObjectTraits::Load<ICommonRoutine>(*strName, *strModule, m_BusinessPtr, *strObject))
    {
        LOGV_INFO(m_pServer->GetFileLog(), TF("[业务管理例程]RTTI load --- Object=%s, Name=%s, Module=%s --- %p"), *strObject, *strName, *strModule, m_BusinessPtr.Get());
        if (m_BusinessPtr->Init() == (UInt)RET_OKAY)
        {
            m_BusinessPtr->OnHandle(PAKE_CONFIG_DEFAULT, reinterpret_cast<uintptr_t>(this), (ULLong)m_pServer);
            return true;
        }
    }
    LOGV_ERROR(m_pServer->GetFileLog(), TF("[业务管理例程]RTTI load --- Object=%s, Name=%s, Module=%s --- Failed!"), *strObject, *strName, *strModule);
    return false;
}

bool CMasterRoutine::SetServiceSocket(UInt uId, ULLong ullParam)
{
    bool bRet = true;

    CSyncLockWaitScope scope(m_ServiceId.Lock);
    ID_MAP& ServiceId = m_ServiceId.GetNextClone();

    ID_MAP_PAIR* pPair = ServiceId.Find(uId);
    if (pPair != nullptr)
    {
        if (pPair->m_V == 0)
        {
            pPair->m_V = ullParam;
        }
        else
        {
            bRet = false;
        }
    }
    else
    {
        bRet = (ServiceId.Add(uId, ullParam) != nullptr);
    }
    if (bRet)
    {
        m_ServiceId.Swap();
    }
    return bRet;
}

bool CMasterRoutine::ClearServiceSocket(UInt uId)
{
    bool bRet = true;

    CSyncLockWaitScope scope(m_ServiceId.Lock);
    ID_MAP& ServiceId = m_ServiceId.GetNextClone();

    ID_MAP_PAIR* pPair = ServiceId.Find(uId);
    if (pPair != nullptr)
    {
        pPair->m_V = 0;
    }
    else
    {
        bRet  = false;
    }
    if (bRet)
    {
        m_ServiceId.Swap();
    }
    return bRet;
}

bool CMasterRoutine::SetGateSocket(UInt uId, ULLong ullParam)
{
    bool bRet = true;

    CSyncLockWaitScope scope(m_GateId.Lock);
    ID_MAP& GateId = m_GateId.GetNextClone();

    ID_MAP_PAIR* pPair = GateId.Find(uId);
    if (pPair != nullptr)
    {
        if (pPair->m_V == 0)
        {
            pPair->m_V = ullParam;
        }
        else
        {
            bRet = false;
        }
    }
    else
    {
        bRet = (GateId.Add(uId, ullParam) != nullptr);
    }
    if (bRet)
    {
        m_GateId.Swap();
    }
    return bRet;
}

bool CMasterRoutine::ClearGateSocket(UInt uId)
{
    bool bRet = true;

    CSyncLockWaitScope scope(m_GateId.Lock);
    ID_MAP& GateId = m_GateId.GetNextClone();

    ID_MAP_PAIR* pPair = GateId.Find(uId);
    if (pPair != nullptr)
    {
        pPair->m_V = 0;
    }
    else
    {
        bRet = false;
    }
    if (bRet)
    {
        m_GateId.Swap();
    }
    return bRet;
}

bool CMasterRoutine::AddServiceRID(ULLong ullParam, ULLong ullId)
{
    SOCKET_RID_MAP_PAIR* pPair = m_SocketRId.Find(ullParam);
    if (pPair == nullptr)
    {
        PINDEX index = m_SocketRId.Add(ullParam);
        if (index != nullptr)
        {
#ifdef __DUMP_STEP_DETAIL__
            DEV_DUMP(TF("[业务管理例程]业务功能[%p]注册第一次业务RID=%llX!"), ullParam, ullId);
#endif
            pPair = m_SocketRId.GetAt(index);
            pPair->m_V.Add(ullId);
        }
    }
    else
    {
#ifdef __DUMP_STEP_DETAIL__
        DEV_DUMP(TF("[业务管理例程]业务功能[%p]注册业务RID=%llX!"), ullParam, ullId);
#endif
        assert(pPair->m_V.Find(ullId) == -1);
        pPair->m_V.Add(ullId);
    }
    return true;
}

bool CMasterRoutine::RemoveServiceRID(ULLong ullParam, ULLong ullId)
{
    SOCKET_RID_MAP_PAIR* pPair = m_SocketRId.Find(ullParam);
    if (pPair != nullptr)
    {
#ifdef __DUMP_STEP_DETAIL__
        DEV_DUMP(TF("[业务管理例程]业务功能[%p]注销业务RID=%llX!"), ullParam, ullId);
#endif
        return pPair->m_V.Remove(ullId);
    }
    return false;
}

bool CMasterRoutine::RemoveServiceAllRID(ULLong ullParam, ARY_ROUTINEID& aRID)
{
    SOCKET_RID_MAP_PAIR* pPair = m_SocketRId.Find(ullParam);
    if (pPair != nullptr)
    {
        aRID = std::move(pPair->m_V);
#ifdef __DUMP_STEP_DETAIL__
        DEV_DUMP(TF("[业务管理例程]业务功能[%p]断开删除所有RID=%d!"), ullParam, aRID.GetSize());
#endif
        m_SocketRId.RemoveAt(reinterpret_cast<PINDEX>(pPair));
        return (aRID.GetSize() > 0);
    }
    return false;
}

Socket_t CMasterRoutine::GetRoutineIdSocket(ULLong ullRoutineId)
{
    RID_SOCKET_MAP_PAIR* pPair = m_RIdSocket.GetCurr().Find(ullRoutineId);
    if (pPair != nullptr)
    {
        Socket_t sSocket = GetSocket(PAKT_SERVICE, (UInt)pPair->m_V.utId);
        if (sSocket == (Socket_t)pPair->m_V.sSocket)
        {
            return sSocket;
        }
    }
    return 0;
}

bool CMasterRoutine::OnTransferAuthor(CEventBase& EventRef, ULLong ullParam)
{
    CNETVisa* pVisa = static_cast<CNETVisa*>(&EventRef);
    if (m_pServer->GetServerInfo(PAKT_MASTER)->IsWarnLoad(false) == false)
    {
        SORT_RESULT& SortResult = m_pServer->GetServerSort(PAKT_GATE)->GetCur();
        if (SortResult.uCount > 0)
        {
            UInt uIndex = SortResult.Index();

            pVisa->SetType(PAKT_MASTER);
            pVisa->Addr(SortResult.Sort[uIndex].Addr);
#ifdef __DUMP_STEP_DETAIL__
            DEV_DUMP(TF("[业务管理例程]业务区签证 --- 选择的业务网关id=%d!"), SortResult.Sort[uIndex].usId);
#endif
            if (pVisa->ResetSend((Socket_t)ullParam, m_pServer->GetNetworkPtr(), pVisa->Length()) == false)
            {
                DEV_ERROR(TF("CMasterRoutine::OnTransferAuthor ResetSend Failed! %d"), pVisa->GetEvent());
            }
        }
        else
        {
#ifdef __DUMP_STEP_DETAIL__
            DEV_WARN(TF("[业务管理例程]业务区签证 --- 没有业务网关在线!"));
#endif
            pVisa->ResetHead((UEVENT)PAKE_AUTHOR_VISAACK, (UTYPE)PAKT_MASTER, (UACK)ROUTINE_ERROR_NONE_GATE);
            pVisa->Send((Socket_t)ullParam, m_pServer->GetNetworkPtr());
        }
    }
    else
    {
#ifdef __DUMP_STEP_DETAIL__
        DEV_WARN(TF("[业务管理例程]业务区签证 --- 业务管理服务器告警或者异常状态[%d]!"), m_pServer->GetServerInfo(PAKT_MASTER)->usLoad);
#endif
        pVisa->ResetHead((UEVENT)PAKE_AUTHOR_VISAACK, (UTYPE)PAKT_MASTER, (UACK)ERRORD_MAYDAY);
        pVisa->Send((Socket_t)ullParam, m_pServer->GetNetworkPtr());
    }
    return true;
}

bool CMasterRoutine::OnLinkRoutineId(CEventBase& EventRef, ULLong ullParam)
{
    CNETSrvLink* pSrvLink = static_cast<CNETSrvLink*>(&EventRef);

    ULLong   ullId  = pSrvLink->GetSessionId();
    CString  strId;
    CStream& Stream = pSrvLink->GetStream();
    if (Stream.IsEnd())
    {
        strId.ToString(ullId);
    }
    else
    {
        // 默认的必须带上UUID, 否则无法连接
        Stream >> strId;
        ullId = CHash::Hash64(*strId);
    }
    UACK uAck = (UACK)ERRORD_OKAY;
    {
        CKVString strRoutineId = *strId;

        CSyncLockWaitScope scope(m_RoutineId.Lock);
        ROUTINEID_MAP& RoutineId = m_RoutineId.GetNextClone();

        ROUTINEID_MAP_PAIR* pPair = RoutineId.Find(ullId);
        if (pPair == nullptr)
        {
            if (m_bEnableLink)
            {
                RoutineId.Add(ullId, std::move(strRoutineId));
                LOGV_DUMP(m_pServer->GetFileLog(), TF("[业务管理例程]OnLinkRoutineId --- UUID=%s[HASH=%llX]成功加入列表"), *strId, ullId);
                m_RoutineId.Swap();
            }
            else
            {
                LOG_WARN(m_pServer->GetFileLog(), TF("[业务管理例程]OnLinkRoutineId --- 业务服务器注册id状态关闭!"));
                uAck = (UACK)ROUTINE_ERROR_LINK_DISABLED;
            }
        }
        else if (pPair->m_V != strRoutineId)// uuid-hash冲突
        {
            LOGV_WARN(m_pServer->GetFileLog(), TF("[业务管理例程]OnLinkRoutineId --- UUID=%s[HASH=%llX]与存在的UUID=%s[HASH=%llX] --- HASH值冲突, %s被忽略, 请重新更换UUID!"), *strId, ullId, *(pPair->m_V), pPair->m_K, *strId);
            uAck = (UACK)ROUTINE_ERROR_LINK_ID;
        }
    }
    if (uAck == (UACK)ERRORD_OKAY)
    {
        CSyncLockWaitScope scope(m_RIdSocket.Lock);
        RID_SOCKET_MAP& RIdSocket = m_RIdSocket.GetNextClone();

        RID_SOCKET_MAP_PAIR* pPair = RIdSocket.Find(ullId);
        if (pPair == nullptr)
        {
            SERVICE_SOCKET ss;
            ss.utId    = (uintptr_t)pSrvLink->GetSrvId();
            ss.sSocket = (Socket_t)ullParam;
            RIdSocket.Add(ullId, ss);
            LOGV_DUMP(m_pServer->GetFileLog(), TF("[业务管理例程]OnLinkRoutineId --- UUID=%s[HASH=%llX][ID=%d]成功注册在线业务服务器Socket=%llX"), *strId, ullId, ss.utId, ullParam);
            m_RIdSocket.Swap();
            //
            AddServiceRID(ullParam, ullId);
        }
        else if (pPair->m_V.sSocket != (Socket_t)ullParam)
        {
            if (pPair->m_V.utId != (uintptr_t)pSrvLink->GetSrvId())
            {
                LOGV_WARN(m_pServer->GetFileLog(), TF("[业务管理例程]OnLinkRoutineId --- UUID=%s[HASH=%llX][ID=%d]注册在线业务服务器Socket=%llX异常 --- 已经存在在线服务器[ID=%d][Socket=%llX]与当前注册服务器不同!"), *strId, ullId, pSrvLink->GetSrvId(), ullParam, pPair->m_V.utId, pPair->m_V.sSocket);
                uAck = (UACK)ROUTINE_ERROR_LINK_ONLINE;
            }
            else
            {
                pPair->m_V.sSocket = (Socket_t)ullParam;
                LOGV_DUMP(m_pServer->GetFileLog(), TF("[业务管理例程]OnLinkRoutineId --- UUID=%s[HASH=%llX][ID=%d]重新注册在线业务服务器Socket=%llX"), *strId, ullId, pPair->m_V.utId, ullParam);
                m_RIdSocket.Swap();
                //
                AddServiceRID(ullParam, ullId);
            }
        }
        else
        {
            assert(pPair->m_V.utId == (uintptr_t)pSrvLink->GetSrvId());
            LOGV_WARN(m_pServer->GetFileLog(), TF("[业务管理例程]OnLinkRoutineId --- UUID=%s[HASH=%llX][ID=%d]注册在线业务服务器Socket=%llX重复!"), *strId, ullId, pPair->m_V.utId, ullParam);
        }
    }
    pSrvLink->SetSessionId(ullId);
    pSrvLink->ResetHead((UEVENT)PAKE_SERVICE_LINKACK, (UTYPE)PAKT_MASTER, uAck);
    pSrvLink->Send((Socket_t)ullParam, m_pServer->GetNetworkPtr());
    if (uAck == (UACK)ERRORD_OKAY)
    {
        // 通知所有网关对应业务功能服务器上线, 网关通知所有外网端可以连接注册
        CPAKSrvLinkAck LinkAck(ROUTINE_ERROR_LINK_ID);
        LinkAck.SetSrvId((USRVID)pSrvLink->GetSrvId());
        LinkAck.SetSessionId(ullId);
        m_pServer->GetNetworkPtr()->Send(0, LinkAck, CNETTraits::SEND_BROADCAST_AS, PAKT_GATE);
    }
    return true;
}

bool CMasterRoutine::OnUnlinkRoutineId(CEventBase& EventRef, ULLong ullParam)
{
    CNETSrvUnlink* pSrvUnlink = static_cast<CNETSrvUnlink*>(&EventRef);

    ULLong ullId = pSrvUnlink->GetSessionId();
    
    CStream& Stream = pSrvUnlink->GetStream();
    if (Stream.IsEnd() == false)
    {
        CString strId;
        Stream >> strId;
        ullId = CHash::Hash64(*strId);
    }
    UACK uAck = (UACK)ERRORD_OKAY;
    {
        CSyncLockWaitScope scope(m_RIdSocket.Lock);
        RID_SOCKET_MAP& RIdSocket = m_RIdSocket.GetNextClone();

        RID_SOCKET_MAP_PAIR* pPair = RIdSocket.Find(ullId);
        if ((pPair != nullptr) && (pPair->m_V.sSocket == (Socket_t)ullParam))
        {
            assert(pPair->m_V.utId == (uintptr_t)pSrvUnlink->GetSrvId());
            RIdSocket.RemoveAt(reinterpret_cast<PINDEX>(pPair));
            m_RIdSocket.Swap();

            RemoveServiceRID(ullParam, ullId);

            LOGV_DUMP(m_pServer->GetFileLog(), TF("[业务管理例程]OnUnlinkRoutineId --- HASH=%llX[ID=%d]对应在线业务服务器从列表删除"), ullId, pSrvUnlink->GetSrvId());
        }
        else
        {
            LOGV_WARN(m_pServer->GetFileLog(), TF("[业务管理例程]OnUnlinkRoutineId --- HASH=%llX[ID=%d]对应在线业务服务器不在列表中!"), ullId, pSrvUnlink->GetSrvId());
            uAck = (UACK)ROUTINE_ERROR_LINK_ONLINE;
        }
    }
    if (uAck == (UACK)ERRORD_OKAY)
    {
        CSyncLockWaitScope scope(m_RoutineId.Lock);
        ROUTINEID_MAP& RoutineId = m_RoutineId.GetNextClone();

        ROUTINEID_MAP_PAIR* pPair = RoutineId.Find(ullId);
        if (pPair != nullptr)
        {
            if (m_bEnableLink)
            {
                LOGV_DUMP(m_pServer->GetFileLog(), TF("[业务管理例程]OnUnlinkRoutineId --- UUID=%s[HASH=%llX]从列表删除"), *pPair->m_V, ullId);
                RoutineId.RemoveAt(reinterpret_cast<PINDEX>(pPair));
                m_RoutineId.Swap();
            }
            else
            {
                LOG_WARN(m_pServer->GetFileLog(), TF("[业务管理例程]OnUnlinkRoutineId --- 业务服务器注销id状态关闭2!"));
                uAck = (UACK)ROUTINE_ERROR_LINK_DISABLED;
            }
        }
        else
        {
            LOGV_WARN(m_pServer->GetFileLog(), TF("[业务管理例程]OnUnlinkRoutineId --- HASH=%llX不在列表中!"), ullId);
            uAck = (UACK)ROUTINE_ERROR_LINK_ID;
        }
    }
    pSrvUnlink->SetSessionId(ullId);
    pSrvUnlink->ResetHead((UEVENT)PAKE_SERVICE_UNLINKACK, (UTYPE)PAKT_MASTER, uAck);
    pSrvUnlink->Send((Socket_t)ullParam, m_pServer->GetNetworkPtr());
    if (uAck == (UACK)ERRORD_OKAY)
    {
        // 通知所有网关对应业务功能服务器上线, 网关通知所有连接的外网端业务停用
        CPAKSrvUnlinkAck UnlinkAck((UACK)ROUTINE_ERROR_LINK_DISABLED);
        UnlinkAck.SetSrvId((USRVID)pSrvUnlink->GetSrvId());
        UnlinkAck.SetSessionId(ullId);
        m_pServer->GetNetworkPtr()->Send(0, UnlinkAck, CNETTraits::SEND_BROADCAST_AS, PAKT_GATE);
    }
    return true;
}

bool CMasterRoutine::OnUnlinkAllRoutineId(ARY_ROUTINEID& aRID, UInt uId, ULLong ullParam)
{
    Int nCount = 0;
    {
        CSyncLockWaitScope scope(m_RIdSocket.Lock);
        RID_SOCKET_MAP& RIdSocket = m_RIdSocket.GetNextClone();
        for (Int i = 0; i < aRID.GetSize(); ++i)
        {
            RID_SOCKET_MAP_PAIR* pPair = RIdSocket.Find(aRID[i]);
            if ((pPair != nullptr) && (pPair->m_V.sSocket == (Socket_t)ullParam))
            {
                assert(pPair->m_V.utId == (uintptr_t)uId);
                RIdSocket.RemoveAt(reinterpret_cast<PINDEX>(pPair));
                ++nCount;
                LOGV_DUMP(m_pServer->GetFileLog(), TF("[业务管理例程]OnUnlinkAllRoutineId --- HASH=%llX[ID=%d]对应在线业务服务器从列表删除"), aRID[i], uId);
            }
            else
            {
                LOGV_WARN(m_pServer->GetFileLog(), TF("[业务管理例程]OnUnlinkAllRoutineId --- HASH=%llX[ID=%d]对应在线业务服务器不在列表中!"), aRID[i], uId);
            }
        }
        if (nCount > 0)
        {
            m_RIdSocket.Swap();
        }
    }
    if ((nCount > 0) && (m_bEnableLink == true))
    {
        nCount = 0;
        CSyncLockWaitScope scope(m_RoutineId.Lock);
        ROUTINEID_MAP& RoutineId = m_RoutineId.GetNextClone();
        for (Int i = 0; i < aRID.GetSize(); ++i)
        {
            ROUTINEID_MAP_PAIR* pPair = RoutineId.Find(aRID[i]);
            if (pPair != nullptr)
            {
                LOGV_DUMP(m_pServer->GetFileLog(), TF("[业务管理例程]OnUnlinkAllRoutineId --- UUID=%s[HASH=%llX]从列表删除"), *pPair->m_V, aRID[i]);
                RoutineId.RemoveAt(reinterpret_cast<PINDEX>(pPair));
                ++nCount;
            }
            else
            {
                LOGV_WARN(m_pServer->GetFileLog(), TF("[业务管理例程]OnUnlinkAllRoutineId --- HASH=%llX不在列表中!"), aRID[i]);
            }
        }
        if (nCount > 0)
        {
            m_RoutineId.Swap();
        }
    }
    return (nCount > 0);
}

bool CMasterRoutine::OnExternalLogout(CEventBase& EventRef, ULLong ullParam)
{
    // 数据端为连接业务服务器的id数组, 依次通知业务服务器
    CNETSrvLogout* pSrvLogout = static_cast<CNETSrvLogout*>(&EventRef);
    CStream& Stream = pSrvLogout->GetStream();
    UInt uSize = 0;
    Stream >> uSize;

    ARY_ROUTINEID aRID;
    aRID.Add((Int)uSize, false);
    Stream.Read(aRID.GetItem(0), sizeof(ULLong) * uSize);

    CNETRoutine Unlink((UEVENT)PAKE_ROUTINE_UNLINK, (UTYPE)PAKT_MASTER);
    Unlink.SetSrvId(pSrvLogout->GetSrvId());
    Unlink.SetSessionId(pSrvLogout->GetSessionId());
    // 通知所有连接的业务功能服务器, 外网端断开
    for (Int i = 0; i < aRID.GetSize(); ++i)
    {
        Socket_t sSocket = GetRoutineIdSocket(aRID[i]);
        if (sSocket != 0)
        {
            Unlink.SetRoutineId(aRID[i]);
            m_pServer->GetNetworkPtr()->Send(sSocket, Unlink);
        }
        else
        {
            LOGV_WARN(m_pServer->GetFileLog(), TF("[业务管理例程]外网端断开 --- 通知连接的业务功能服务器RID=%llX不在线!"), aRID[i]);
        }
    }
    return true;
}

bool CMasterRoutine::OnTransferRoutine(Int nEvent, CEventBase& EventRef, ULLong ullParam)
{
    if ((nEvent >= PAKE_ROUTINE_LINK) && (nEvent < PAKE_ROUTINE_LINKACK)) // 外网端发送请求到对应业务
    {
        CNETRoutine* pRoutine = static_cast<CNETRoutine*>(&EventRef);
        if (pRoutine->GetType() == PAKT_GATE)
        {
            pRoutine->SetType(PAKT_MASTER);

            UACK uAck = (UACK)ERRORD_OKAY;
            Socket_t sSocket = GetRoutineIdSocket(pRoutine->GetRoutineId());
            if (sSocket != 0)
            {
                if (sSocket == (Socket_t)ullParam) // 防止自循环
                {
                    LOGV_WARN(m_pServer->GetFileLog(), TF("[业务管理例程]OnTransferRoutine --- [RID=%llX]中转异常, 业务事件=%d业务服务器自循环!"), pRoutine->GetRoutineId(), nEvent);
                    return false;
                }
                if (pRoutine->ResetSend(sSocket, m_pServer->GetNetworkPtr(), pRoutine->Length()) == false)
                {
                    DEV_ERROR(TF("CMasterRoutine::OnTransferRoutine ResetSend Failed! %d"), pRoutine->GetEvent());
                }
                return true;
            }
            else if (m_RIdSocket.GetCurr().Find(pRoutine->GetRoutineId()) != nullptr)
            {
                uAck = (UACK)ROUTINE_ERROR_REQ_SERVICE;  // 等待上线通知
            }
            else
            {
                uAck = (UACK)ROUTINE_ERROR_VOIDNESS_RID; // 业务功能未注册过, 请等待上线再重新连接
            }
            LOGV_WARN(m_pServer->GetFileLog(), TF("[业务管理例程]OnTransferRoutine --- [RID=%llX]中转异常=%d!"), pRoutine->GetRoutineId(), uAck);
            pRoutine->ResetHead((UEVENT)(nEvent + (PAKE_ROUTINE_UNLINKACK - PAKE_ROUTINE_UNLINK)), (UTYPE)PAKT_MASTER, uAck);
            pRoutine->Send(ullParam, m_pServer->GetNetworkPtr());
        }
        else
        {
            LOGV_WARN(m_pServer->GetFileLog(), TF("[业务管理例程]OnTransferRoutine --- [RID=%llX]中转异常, 业务事件=%d对应服务器类型错误=%d!"), pRoutine->GetRoutineId(), nEvent, pRoutine->GetType());
        }
    }
    else if ((nEvent >= PAKE_ROUTINE_LINKACK) && (nEvent <= PAKE_ROUTINE_FAIL))
    {
        CNETRoutine* pRoutine = static_cast<CNETRoutine*>(&EventRef);
        if (pRoutine->GetType() == PAKT_SERVICE)
        {
            pRoutine->SetType(PAKT_MASTER);

            Socket_t sSocket = GetSocket(PAKT_GATE, (UInt)pRoutine->GetSrvId());
            if (sSocket != 0)
            {
                if (sSocket == (Socket_t)ullParam) // 防止自循环
                {
                    LOGV_WARN(m_pServer->GetFileLog(), TF("[业务管理例程]OnTransferRoutine --- [RID=%llX]中转异常, 业务事件=%d网关服务器自循环!"), pRoutine->GetRoutineId(), nEvent);
                    return false;
                }
                if (pRoutine->ResetSend(sSocket, m_pServer->GetNetworkPtr(), pRoutine->Length()) == false)
                {
                    DEV_ERROR(TF("CMasterRoutine::OnTransferRoutine2 ResetSend Failed! %d"), pRoutine->GetEvent());
                }
                return true;
            }
            LOGV_WARN(m_pServer->GetFileLog(), TF("[业务管理例程]OnTransferRoutine --- [RID=%llX]中转对应网关id==%d不在线!"), pRoutine->GetRoutineId(), pRoutine->GetSrvId());
        }
        else
        {
            LOGV_WARN(m_pServer->GetFileLog(), TF("[业务管理例程]OnTransferRoutine --- [RID=%llX]中转异常, 业务事件=%d对应服务器类型错误=%d!"), pRoutine->GetRoutineId(), nEvent, pRoutine->GetType());
        }
    }
    return false;
}

#if (__PLATFORM_TARGET__ == PLATFORM_TARGET_WINDOWS)
// Windows Realse版本MSVC编译器如果不强制调用静态常量, 静态常量会被优化掉
BOOL WINAPI DllMain(HMODULE hModule, ULong ulReason, void* lpReserved)
{
    UNREFERENCED_PARAMETER( lpReserved );

    if (ulReason == DLL_PROCESS_ATTACH)
    {
        ::DisableThreadLibraryCalls(hModule);
        DEV_DUMP(TF("$ Link %s.dll --- DLL_PROCESS_ATTACH"), CMasterRoutine::ms_RTTI.GetName());
    }
    else if (ulReason == DLL_PROCESS_DETACH)
    {
        DEV_DUMP(TF("$ Unlink %s.dll --- DLL_PROCESS_DETACH"), CMasterRoutine::ms_RTTI.GetName());
    }
    return TRUE;
}
#endif
